#pragma once
#include"MY_API.hpp"
#include"online_manage_module.hpp"
#include"mysql_module.hpp"

#include<vector>
#include<set>

#define BOARD_ROW 15
#define BOARD_COL 15
#define CHESS_WHITE 1
#define CHESS_BLACK 2

#include"Ai_Robot.hpp"

enum room_stat{ game_continue = 1, game_over};

class room{
private:
    int _white_id;
    int _black_id;
    int _cur_op_id;
    int _winner_id;
    unsigned int _room_id;
    int _room_status;
    int _num_of_room;
    int all_operator_num;
    user_table *_user;
    online_manage *_net_user;
    std::vector<std::vector<int>> _board;
    std::vector<int> _viewer;
    std::string _board_record[BOARD_ROW];
    std::set<int> is_save_data;
    std::string _board_history_data;
    robot ai;

    bool is_five_line(int row, int col, int row_offset, int col_offset, int color)
    {
        int count = 1;
        int cur_row = row + row_offset;
        int cur_col = col + col_offset;
        while(cur_row >= 0 && cur_row < BOARD_ROW && cur_col >= 0 
            && cur_col < BOARD_COL && _board[cur_row][cur_col] == color)
        {
            count++;
            cur_row += row_offset;
            cur_col += col_offset;
        }

         cur_row = row - row_offset;
         cur_col = col - col_offset;
        while(cur_row >= 0 && cur_row < BOARD_ROW && cur_col >= 0 
            && cur_col < BOARD_COL && _board[cur_row][cur_col] == color)
        {       
            count++;
            cur_row -= row_offset;
            cur_col -= col_offset;
        }

        if (count >= 5) return true;
        return false;
    }

    int is_game_win(int row, int col, int color)
    {
        if ( is_five_line(row,col, 1, 0, color) || is_five_line(row, col, 0, 1, color) ||
             is_five_line(row,col, -1, 1, color) || is_five_line(row, col, -1, -1, color)
        ) return color == CHESS_WHITE ? _white_id: _black_id;

        return 0;
    }

public:
    room(unsigned int room_id, user_table *user, online_manage *net_user)
    :_room_id(room_id), _user(user), _net_user(net_user), _winner_id(0),
    _num_of_room(0), _room_status(game_continue), 
    all_operator_num(0),
    _board(BOARD_ROW, std::vector<int>(BOARD_COL)) 
    {
        for (int i = 0; i < BOARD_ROW; i++)
        {
            _board_record[i].resize(BOARD_COL, '0');
        }
    }

    ~room(){}

    int get_white_uid() {return _white_id;}
    int get_cur_op_id() {return _cur_op_id;}
    int get_black_uid() {return _black_id;}
    int get_winner_uid() {return _winner_id;}
    int get_room_num() {return _num_of_room;}
    int get_room_status() {return _room_status;}
    unsigned int get_room_id(){ return _room_id;}
    void add_white_user(int uid) { _white_id = uid;  _cur_op_id = _white_id; if(uid > 0) _num_of_room++; }
    void add_black_user(int uid) { _black_id = uid; if(uid > 0) _num_of_room++; }
    void add_viewer(int uid){_viewer.push_back(uid); _num_of_room++;}
    std::string get_board_msg()
    {
        std::string board_msg;
        for (int i = 0; i < BOARD_ROW; i++)
        {
            board_msg += _board_record[i];
        }
        return board_msg;
    }

    void sava_board_data(int board_row, int board_col)
    {
        _board_history_data += std::to_string(board_row);
        _board_history_data +=",";
        _board_history_data += std::to_string(board_col);
        _board_history_data +=";";
    }

    //处理下棋操作
    Json::Value handle_chess(Json::Value &req)
    {
        Json::Value resp = req;
        int cur_uid = req["uid"].asInt();
        if (_black_id > 0) _cur_op_id = _cur_op_id == _white_id ? _black_id : _white_id;
        else _cur_op_id = _white_id;
        resp["if_game_over"] = false;

        //1.判断房间两个玩家是否都在线，否则另一方直接获胜
        if (_white_id > 0 && _black_id > 0 && (!_net_user->is_in_room(_white_id) || !_net_user->is_in_room(_black_id)))
        {
            resp["result"] = true;
            resp["cur_op_id"] = _cur_op_id;
            resp["reason"] = "对方已退出，恭喜您获胜";
            _winner_id = _net_user->is_in_room(_white_id) ? _white_id : _black_id;
            resp["winner"] = _winner_id;
            _room_status = game_over;
            resp["if_game_over"] = true;
            return resp;
        }

        //2.判断当前走的位置是否合理
        int board_row = req["row"].asInt();
        int board_col = req["col"].asInt();
        if (_board[board_row][board_col] != 0)
        {
            resp["result"] = false;
            resp["reason"] = "当前棋子位置已被占用";
            return resp;
        }

        //3.判断是否有玩家获胜
        int cur_color = cur_uid == _white_id ? CHESS_WHITE : CHESS_BLACK;
        //用于服务器判断棋局情况
        _board[board_row][board_col] = cur_color;
        //棋局操作数加1，超过225时，即为平局
        all_operator_num++;
        //保存棋盘的数据
        _board_record[board_row][board_col] = cur_color + '0';

        //保存棋盘的历史数据，用于复盘
        sava_board_data(board_row, board_col);
        
        _winner_id = is_game_win(board_row, board_col, cur_color);
        if (_winner_id != 0)
        {
            resp["reason"] = "恭喜您五子连珠，获得胜利";
            _room_status = game_over;
            resp["if_game_over"] = true;
        }

        if (_black_id < 0)
        {
            if (_winner_id == 0)
            {
                ai.re_calculate_score(_board);
                pos ai_pos = ai.get_max_score_pos();
                resp["ai_row"] = ai_pos.row;
                resp["ai_col"] = ai_pos.col;
                _board[ai_pos.row][ai_pos.col] = CHESS_BLACK;
                all_operator_num++;
                _board_record[ai_pos.row][ai_pos.col] = CHESS_BLACK + '0';

                //保存人机历史数据
                sava_board_data(ai_pos.row, ai_pos.col);
                
                //人机走后判断游戏是否结束
                _winner_id = is_game_win(ai_pos.row, ai_pos.col, CHESS_BLACK);
            }

            std::string robot_board_msg = get_board_msg();
            resp["board_msg"] = robot_board_msg;
        
            if (_winner_id != 0)
            {
                _room_status = game_over;
                resp["if_game_over"] = true;
            }
        }

        if (all_operator_num >= 225 && _winner_id == 0)
        {
            
            resp["reason"] = "强者对局，不分伯仲";
            resp["if_game_over"] = true;
            _room_status = game_over;
        }

        //std::cout << "当前已操作步数 "<<all_operator_num <<std::endl;
        resp["cur_op_id"] = _cur_op_id;
        resp["optype"] = "put_chess";
        resp["result"] = true;
        resp["winner"] = _winner_id;
        return resp;
    }

    //处理聊天操作
    Json::Value handle_chat(Json::Value &req)
    {
        Json::Value resp = req;
        //这里可以扩展关键词屏蔽功能
        resp["result"] = "true";
        return resp;
    }

    //处理玩家退出房间操作
    void handle_exit(int uid)
    {
        //如果游戏尚未结束玩家退出则另一方赢，否则正常退出
        Json::Value resp;
        //std::cout << "当前游戏房间状态 "<< _room_status << "当前用户id" << uid << std::endl;
        if (_room_status == game_continue && (uid == _black_id || uid == _white_id))
        {
            //std::cout<< "开始判断哪个玩家获胜了" << std::endl;
            _winner_id = uid == _white_id ? _black_id : _white_id;
            resp["black_id"] = _black_id;
            resp["white_id"] = _white_id;
            resp["optype"] = "put_chess";
            resp["result"] = true;
            resp["reason"] = "对方退出，恭喜您获得胜利";
            resp["room_id"] = _room_id;
            resp["uid"] = uid;
            resp["row"] = -1;
            resp["col"] = -1;
            resp["winner"] = _winner_id;
            int loser_id = _winner_id == _white_id ? _black_id : _white_id;
            if(_winner_id > 0)_user->win(_winner_id);
            if(loser_id > 0) _user->lose(loser_id);
            _room_status = game_over;
            broadcast(resp);
        }

        Json::Value insert_data;
        auto it = is_save_data.find(uid);
        if (it != is_save_data.end())
        {
            int color = 0;
            if (uid == _white_id) color = CHESS_WHITE;
            else if (uid == _black_id) color = CHESS_BLACK;
            else color = 3;

            insert_data["fight_uid"] = uid;
            insert_data["fight_color"] = color;
            insert_data["fight_win"] = get_winner_uid();
            insert_data["fight_data"] = _board_history_data.c_str();
            insert_data["fight_op_num"] = all_operator_num;
            _user->insert_user_data(insert_data);

            is_save_data.erase(uid);
        }
        _num_of_room--;
        return;
    }

    //请求分配，根据请求，调用不同处理函数，并广播给其它用户
    void handle_request(Json::Value &req)
    {
        Json::Value resp;
        //1.判断请求房间号与当前房间号是否匹配
        int room_id = req["room_id"].asUInt64();
        if (room_id != _room_id)
        {
            resp["optype"] = "put_chess";
            resp["result"] = false;
            resp["reason"] = "房间号不匹配";
        }

        //2.根据请求类型，调用对应方法
        std::string request_type = req["optype"].asString();
        if (request_type == "put_chess")
        {
            resp = handle_chess(req);
            if (resp["winner"].asInt() != 0) {
                int winner_id = resp["winner"].asInt();
                int loser_id = winner_id == _white_id ? _black_id : _white_id;
                if(winner_id > 0) _user->win(winner_id);
                if(loser_id > 0)_user->lose(loser_id);
                _room_status = game_over;
            }
        }
        else if (request_type == "chat")
        {
            resp = handle_chat(req);
        }
        else if (request_type == "save_data")
        {
            is_save_data.insert(req["uid"].asInt());
        }
        else{
            resp["optype"] = request_type.c_str();
            resp["result"] = false;
            resp["reason"] = "我是服务端：并不知道请求类型是啥？";
        }

        resp["white_id"] = get_white_uid();
        resp["black_id"] = get_black_uid();
        std::string room_broadcast_message;
        Json_API::serialize(resp, room_broadcast_message);
        //log_message(NORMAL, "房间请求信息广播：%s", room_broadcast_message.c_str());
        return broadcast(resp);
    }

    //把指定信息广播给其它用户
    void broadcast(Json::Value &req)
    {
        //1. 对要响应的信息进行序列化，将Json::Value中的数据序列化成为json格式字符串
        std::string body;
        Json_API::serialize(req, body);

        //2. 聊天消息只能给下棋者广播
        std::string request_type = req["optype"].asString();
        if (request_type == "chat" && _room_status == game_continue)
        {
            web_server_t::connection_ptr wconn = _net_user->get_connect_from_room(_white_id);
            if (wconn.get() != nullptr) {
                wconn->send(body);
            }else {
                log_message(ERROR, "房间-白棋玩家连接获取失败");
            }
            web_server_t::connection_ptr bconn = _net_user->get_connect_from_room(_black_id);
            if (bconn.get() != nullptr) {
                bconn->send(body);
            }else {
                log_message(ERROR, "房间-黑棋玩家连接获取失败");
            }

            return;
        }

        //给白子发送消息
        web_server_t::connection_ptr white_con = _net_user->get_connect_from_room(_white_id);
        if (white_con.get() != nullptr) {
                white_con->send(body);
        }

        //给黑子发送消息
        web_server_t::connection_ptr black_con = _net_user->get_connect_from_room(_black_id);
        if (black_con.get() != nullptr) {
            black_con->send(body);
        }

        //获取房间中所有观战用户的通信连接发送响应信息
        for (int i = 0; i < _viewer.size(); i++)
        {
            //std::string board_msg = get_board_msg();
            //req["board_msg"] = board_msg.c_str();
            //Json_API::serialize(req, body);
            //std::cout << body << std::endl;
            web_server_t::connection_ptr connecter = _net_user->get_connect_from_room(_viewer[i]);
            if (connecter.get() != nullptr) {
                connecter->send(body);
            }
        }
        return;
    }  
};

using room_ptr = std::shared_ptr<room>;

class room_manage{
private:
    unsigned int _next_room_id;
    std::mutex _mutex;
    user_table *_user_rm;
    online_manage *_net_user_rm;
    std::unordered_map<int, room_ptr> _rooms;
    std::unordered_map<int, int> _room_with_users;
public:
//初始化房间计数器
    room_manage(user_table *user_rm, online_manage *net_user_rm)
    :_next_room_id(1), _user_rm(user_rm), _net_user_rm(net_user_rm){}
    ~room_manage(){}

//为两个用户创建一个房间，并返回房间的智能指针管理对象
    room_ptr create_room(int uid_1, int uid_2)
    {
        //校验两个用户是否都还在游戏大厅中，只有都在才需要创建房间。
        if (_net_user_rm->is_in_hall(uid_1) == false) {
            log_message(ERROR, "用户：%d 不在大厅中，创建房间失败!", uid_1);
            return room_ptr();
        }

        if (_net_user_rm->is_in_hall(uid_2) == false) {
            log_message(ERROR, "用户：%d 不在大厅中，创建房间失败!", uid_2);
            return room_ptr();
        }

        //开始创建房间
        std::unique_lock<std::mutex> _mt(_mutex);
        room_ptr rm(new room(_next_room_id, _user_rm, _net_user_rm));
        if (rm.get() == nullptr)
        {
            log_message(ERROR, "游戏房间创建失败");
            return room_ptr();
        }
        
        //把两名玩家给添加到房间中
        if (uid_1 < 1 || uid_2 < 1)
        {
            if(uid_1 < 1) {rm->add_black_user(uid_1); rm->add_white_user(uid_2);}
            else {rm->add_black_user(uid_2); rm->add_white_user(uid_1);}
        }
        else
        {
            rm->add_white_user(uid_1);
            rm->add_black_user(uid_2);
        }

        //把房间和玩家的信息进行绑定，方便管理
        _rooms.insert(std::make_pair(_next_room_id, rm));
        if(uid_1 > 0) _room_with_users.insert(std::make_pair(uid_1, _next_room_id));
        if(uid_2 > 0) _room_with_users.insert(std::make_pair(uid_2, _next_room_id));
        _next_room_id++;
        log_message(NORMAL, "游戏房间创建成功"); 
        return rm;  
    }

//把观战用户添加到游戏房间中
void add_viewer_users(int uid, uint64_t room_id)
{      
        //把房间和玩家的信息进行绑定，方便管理
        _room_with_users.insert(std::make_pair(uid, room_id));    
        log_message(NORMAL, "观战用户加入房间成功"); 
}


//通过房间ID获取房间信息
    room_ptr get_room_by_roomid(unsigned int room_id)
    {
        auto pos = _rooms.find(room_id);
        if (pos == _rooms.end())
        {
            return room_ptr();
        }
        return pos->second;
    }

//通过用户ID获取房间信息
    room_ptr get_room_by_uid(int uid)
    {
        auto pos_uid = _room_with_users.find(uid);
        if (pos_uid == _room_with_users.end())
        {
            return room_ptr();
        }

        //std::cout << "通过用户id找到了房间id" << "房间id为："<< pos_uid->second << std::endl;
        auto pos_rid =  _rooms.find(pos_uid->second);
        if (pos_rid == _rooms.end())
        {
            return room_ptr();
        }

        //std::cout << "通过用户房间id,找到了房间管理指针" << std::endl;
        return pos_rid->second;
    }

//通过房间ID销毁房间
    void destory_room(unsigned int room_id)
    {
        auto pos = _rooms.find(room_id);
        if (pos == _rooms.end())
        {
            return;
        }

        //先要删除房间中的两个用户，然后才能销毁房间
        int uid_1 = pos->second->get_black_uid();
        int uid_2 = pos->second->get_white_uid();

        std::unique_lock<std::mutex> _mt(_mutex);
        if(uid_1 > 0) _room_with_users.erase(uid_1);
        if(uid_2 > 0) _room_with_users.erase(uid_2);

        _rooms.erase(room_id);
    }

//删除房间中的指定用户，如果没有用户了，则销毁整个房间，用户断开连接时调用
    void remove_room_user(int uid)
    {
        auto pos_uid = _room_with_users.find(uid);
        if (pos_uid == _room_with_users.end())
        {
            return;
        }

        //std::cout<< "在游戏房间中找到该用户，准备移除" << std::endl;
        room_ptr _rp = get_room_by_uid(uid);
        if (_rp.get() == nullptr) return;
            _rp->handle_exit(uid);

        int white_id = _rp->get_white_uid();
        int black_id = _rp->get_black_uid(); 

        if (uid != white_id && uid != black_id)
        {
            _room_with_users.erase(pos_uid);
        }

        if (_rp->get_room_num() == 0)
        {
            log_message(NORMAL, "在游戏房间人数为0，准备删掉房间");
            destory_room(_rp->get_room_id());
        }
    }
};
